use rocket::{
  delete, get,
  http::Status,
  post, put,
  response::status::{Custom, NoContent},
  serde::json::{serde_json::json, Json, Value},
};

use crate::{
  db::DbConn,
  error::server_error,
  mappers::todo::TodoMapper,
  models::todo::{NewTodo, UpdateTodo},
};

// docker-compose exec app curl 127.0.0.1:8000/todo
#[get("/todo")]
pub async fn get_todo(db: DbConn) -> Result<Value, Custom<Value>> {
  db.run(|conn| {
    TodoMapper::find_multiple(conn, 100)
      .map(|result| json!(result))
      .map_err(|err| server_error(&err))
  })
  .await
}

// docker-compose exec app curl 127.0.0.1:8000/todo/1
#[get("/todo/<id>")]
pub async fn view_todo(db: DbConn, id: i32) -> Result<Value, Custom<Value>> {
  db.run(move |conn| {
    TodoMapper::find(conn, id)
      .map(|result| json!(result))
      .map_err(|err| server_error(&err))
  })
  .await
}
// docker-compose exec app curl 127.0.0.1:8000/todo -X POST -H "Content-Type: application/json" - d '{"name": "abc", "email": "abc@qq.com"}'
#[post("/todo", format = "json", data = "<new_todo>")]
pub async fn create_todo(
  db: DbConn,
  new_todo: Json<NewTodo>,
) -> Result<Custom<Value>, Custom<Value>> {
  db.run(|conn| {
    TodoMapper::create(conn, new_todo.into_inner())
      .map(|result| Custom(Status::Created, json!(result)))
      .map_err(|err| server_error(&err))
  })
  .await
}

// docker-compose exec app curl 127.0.0.1:8000/todo/1 -X PUT -H "Content-Type: application/json" -d '{"name": "ABC", "email": "ABC@qq.com"}'
#[put("/todo/<id>", format = "json", data = "<update_todo>")]
pub async fn update_todo(
  db: DbConn,
  id: i32,
  update_todo: Json<UpdateTodo>,
) -> Result<Value, Custom<Value>> {
  db.run(move |conn| {
    TodoMapper::update(conn, id, update_todo.into_inner())
      .map(|result| json!(result))
      .map_err(|err| server_error(&err))
  })
  .await
}

// docker-compose exec app curl 127.0.0.1:8000/todo/1 -X DELETE
#[delete("/todo/<id>")]
pub async fn delete_todo(
  db: DbConn,
  id: i32,
) -> Result<NoContent, Custom<Value>> {
  db.run(move |conn| {
    TodoMapper::delete(conn, id)
      .map(|_| NoContent)
      .map_err(|err| server_error(&err))
  })
  .await
}
